#!/usr/bin/perl
#
# $Id$
#
use SNMP;
use Data::Dumper;
#
# command line parsing
require 'getopt.pl';

do Getopt('');	# no switches take arguments.  Just expect "-a" for autofix
$do_autofix = $opt_a;
 
SNMP::initMib();
SNMP::addMibFiles("orinoco.mib");
if ($opt_d) {
	$SNMP::debugging = 1;
}

# want to see output as it happens
$|=1;

open(APLIST, "aplist") || die "aplist: $!\n";
while (<APLIST>) {
	next if (/^#/ || /^\s*$/);
	chop;
	($ap, $community, $wcommunity, $group) = split(/\t/);
	if ($ap eq "default") {
		$defcomm = $community;
		next;
	}
	if ($ap eq "write") {
		$defwcomm = $community;
		next;
	}
	push(@aplist, $ap);
	$community{$ap} = $community || $defcomm;
	$wcommunity{$ap} = $wcommunity || $defwcomm;
	$group{$ap} = $group || "default";
}
close(APLIST);

open(VARLIST, "varlist") || die "varlist: $!\n";
while (<VARLIST>) {
	next if (/^#/ || /^\s*$/);
	chop;
	($var, $expectedval, $descr, $autofix) = split(/\t/);
	push (@varlist, $var);
	$expectedval{$var} = $expectedval;
	$descr{$var} = $descr;
	$autofix{$var} = $autofix;
}
close(VARLIST);

if (open(VARSPERDEV, "varsperdev")) {
	while (<VARSPERDEV>) {
		next if (/^#/ || /^\s*$/);
		chop;
		($ap, @vars) = split(/\t/);
		if ($ap eq "vars") {
			@customapvars = @vars;
			next;
		}
		for ($i = 0; $i <= $#customapvars; $i++) {
			$apspecific{$ap.$customapvars[$i]} = $vars[$i];
		}
	}
	close(VARSPERDEV);
} else {
	warn("varsperdev: $!\n");
}

if (open(VARSPERGROUP, "varspergroup")) {
	while (<VARSPERGROUP>) {
		next if (/^#/ || /^\s*$/);
		chop;
		($group, @vars) = split(/\t/);
		if ($group eq "vars") {
			@customgroupvars = @vars;
			next;
		}
		for ($i = 0; $i <= $#customgroupvars; $i++) {
			$groupspecific{$group.$customgroupvars[$i]} = $vars[$i];
		}
		push(@grouplist, $group);
	}
	close(VARSPERGROUP);
} else {
	warn("varspergroup: $!\n");
}

if (@ARGV) {
	@oldaplist = @aplist;
	@aplist = ();
	foreach $arg (@ARGV) {
		if (defined($community{$arg})) {
			push (@aplist, $arg);
		} else {
			warn "${arg}: unknown access point\n";
		}
	}
}

print qq`<html>
<head>
<title>Access Point Configuration Matrix</title>
</head>
<body bgcolor="white">
<h1>Access Point Configuration Matrix</h1>
<p>
This table generated at `, scalar(localtime(time)), qq`.
<p>
Each access point has a row; the columns are the variables being
monitored.  Black text means the column is just informational;
green means it's consistent with the desired variable and red
means it's inconsistent.  At the <a href="#variables">bottom of this page</a>
is a list of the variables being monitored.
`;
print "<table border>\n";
print "<tr><th>Access Point</th>\n";
foreach $var (@varlist) {
	print "<th>", $descr{$var} || $var, "</th>\n";
}
print "</tr>\n";
foreach $ap (@aplist) {
	print "<tr><td>$ap</td>\n";
	$snmpw = undef;
	$snmp = new SNMP::Session(DestHost => $ap, Community => $community{$ap}, UseEnums => 1);
	# "ping" so if it's unreachable we don't time out for each variable
	$test = $snmp->get("sysDescr.0");
	if (!defined($test)) {
		print "<td colspan=", scalar(@varlist), "><i>timeout</i></td></tr>\n";
		next;
	}
	foreach $var (@varlist) {
		$val = $snmp->get($var);
		$val = "<i>timeout</i>" if (!defined($val));
		($noinstance = $var) =~ s/\.\d+$//;
		print "<!-- $var " . SNMP::getType($noinstance) . " -->\n";
		# sigh, getType doesn't help.
		# Everything's an OCTETSTR.
		if ($noinstance eq 'ifPhysAddress') {
			$val = join(":", grep(($_ = sprintf("%x", $_), 1), unpack("C*", $val)));
		}
		print "<td align=center>";
		if ($snmp->{ErrorStr} =~ "noSuchName") {
			print "&nbsp;";
		} else {
		$expected = $apspecific{$ap.$var} || $groupspecific{$group{$ap}.$var} || $expectedval{$var};
		if ($expected) {
			if ($expected =~ m|^(!)?/|) {
				$wasbang = $1;
				$re = $expected;
				$re =~ s|^(!)?/||;
				$re =~ s|/$||;
				$match = $val =~ $re;
				$match = !$match if ($wasbang);
			} else {
				$match = $val eq $expected;
			}
			if ($match == 0) {
				print "<font color=\"red\">";
				print "want $expected -- got ";
			} else {
				print "<font color=\"green\">";
			}
		}
		print (($snmp->{ErrorNum} == 0) ? $val : ("<i>", shorterror($snmp->{ErrorStr}), "</i>"));
		print "</font>" if ($expected);
		if ($do_autofix && $match == 0) {
			if (!$expected || $expected =~ m|^(!)?/| || $autofix{$var} =~ /^n/i || $snmp->{ErrorNum}) {
				print "-&gt; not fixed\n";
			} else {
				print "<br>I'd like to fix this\n";
				if (!$snmpw) {
					$snmpw = new SNMP::Session(DestHost => $ap, Community => $wcommunity{$ap}, UseEnums => 1);
				}
				$ret = $snmpw->set($var,$expected);
				if (!defined($ret)) {
					print "<br>set ($var, $expected) returned ", $snmpw->{ErrorStr};
				} else {
					print "<br>set returned ", $ret || "success";
				}
			}
		}
		}
		print "</td>\n";
	}
	print "</tr>\n";
}
print "</table>\n";
print "<a name=\"variables\">\n";
print "<table border>\n";
print "<tr><th>Variable Name</th><th>Expected Value</th><th>OID</th></tr>\n";
foreach $var (@varlist) {
	print "<tr><td>";
	print $descr{$var} || $var;
	print "</td>\n<td>";
	print $expectedval{$var} || "<i>none</i>";
	print "</td>\n<td>";
	print $var;
	print "</td></tr>\n";
}
print "</table>\n";
print "<p>Per-Group variables:\n";
print "<table border>\n";
print "<tr><th>Group Name</th>";
print "<th>", join("</th>\n<th>", @customgroupvars), "</th></tr>\n";
foreach $group (@grouplist) {
	print "<tr>";
	print "<td>$group</td>\n";
	foreach $var (@customgroupvars) {
		print "<td>", $groupspecific{$group.$var} || "&nbsp;", "</td>\n";
	}
	print "</tr>\n";
}
print "</table>\n";
print "<p>Per-AP variables:\n";
print "<table border>\n";
print "<tr><th>AP Name</th>";
print "<th>", join("</th>\n<th>", @customapvars), "</th></tr>\n";
foreach $ap (@aplist) {
	$row = "<tr><td>$ap</td>\n";
	$gotsomething = 0;
	foreach $var (@customapvars) {
		$row .= "<td>" . ($apspecific{$ap.$var} || "&nbsp;") . "</td>\n";
		$gotsomething = 1 if $apspecific{$ap.$var};
	}
	print $row, "</tr>\n" if ($gotsomething);
}
print "</table>\n";

print qq`
<p>
<div style="background-color: #414196; color: white; border-style: groove">
<img style="float: right" src="palmtree.jpg" alt="[Palm Tree]">
(Peggy says that no web page is complete without a palm tree...)
<div style="clear: both"></div>
</div>
`;
print "</body></html>\n";

sub shorterror($) {
	my($errstr) = shift;

	if ($errstr =~ /\(([^)]+)\)/) {
		return $1;
	}
	return $errstr;
}
